import ply.lex as lex
import ply.yacc as yacc
import copy
import operator

from Lexer import *

def ParsePtrOperator(variable, tokens):
    if tokens[0] == '&':
        variable.reference = True
    elif tokens[0] == '*':
        variable.pointer = True
        if len(tokens) > 1 and tokens[1] == 'const':
            variable.const_pointer = True

def ParseVariable(variable, token_list):
#   import pdb
#   pdb.set_trace()
    tokens = copy.copy(token_list)
    
    if 'const' in tokens:
        variable.const = True
        tokens = filter (lambda token : token != 'const', tokens)
   
    assert len(tokens) == 1
    variable.type = tokens[0]

def ParseClassMembers(class_struct, member_list):
    visibility = class_struct.default_visibility
    for member in member_list:
        if isinstance(member, StructBase):
            if visibility:
                class_struct.member_list.append(member)
        elif member == 'public':
            visibility = True
        elif member == 'protected' or member == 'private':
            visibility = False

# http://linuxsoftware.co.nz/cppgrammar.html

######################################################################
# Programs
######################################################################

def p_translation_unit(p):
    '''
    translation_unit :
                     | declaration_seq
    '''
    if len(p) == 1:
        p[0] = []
    elif len(p) == 2:
        p[0] = p[1]

######################################################################

######################################################################
# Classes
######################################################################

def p_class_specifier(p):
    '''
    class_specifier : class_head class_body
                    | template_declaration class_head class_body
    '''
    index = -1
    if len(p) == 3:
        p[0] = p[1]
        ParseClassMembers(p[0], p[2])
    elif len(p) == 4:
        p[0] = p[2]
        p[0].template_parameter_list = p[1]
        ParseClassMembers(p[0], p[3])

def p_class_body(p):
    '''
    class_body : '{' '}'
               | '{' member_specification '}'
    '''
    if len(p) == 3:
        p[0] = []
    elif len(p) == 4:
        p[0] = p[2]

def p_class_head(p):
    '''
    class_head : class_key
               | class_key ID
               | class_key ID base_clause
    '''
    if len(p) == 2:
        p[0] = ClassStruct()
    elif len(p) == 3:
        p[0] = ClassStruct(name = p[2])
    elif len(p) == 4:
        p[0] = ClassStruct(name = p[2], parent_list = p[3])

    if p[1] == 'class':
        p[0].default_visibility = False
    elif p[1] == 'struct':
        p[0].default_visibility = True
    else:
        assert 0

def p_member_specification(p):
    '''
    member_specification : member_declaration
                         | member_declaration member_specification
                         | access_specifier ':'
                         | access_specifier ':' member_specification
    '''
    if len(p) == 2:
        p[0] = p[1]
    if len(p) == 3:
        if p[2] != ':':
            p[0] = p[1] + p[2]
        else:
            p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = [p[1]] + p[3]

def p_member_declaration(p):
    '''
    member_declaration : ';'
                       | member_declarator_list ';'
                       | decl_specifier_seq member_declarator_list ';'
    '''
    if len(p) == 2:
        p[0] = []
    elif len(p) == 3:
        p[0] = p[1]
    elif len(p) == 4:
        for item in p[2]:
            item.parse_decl_specifier_seq(p[1])
        p[0] = p[2]

    assert isinstance(p[0], list)
    for item in p[0]:
        assert isinstance(item, StructBase)

def p_member_declarator_list(p):
    '''
    member_declarator_list : member_declarator
                           | member_declarator_list ',' member_declarator
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = p[1] + [p[3]]
    
    p[0] = filter(lambda item : item, p[0])

    assert isinstance(p[0], list)
    for item in p[0]:
        assert isinstance(item, StructBase)

def p_member_declarator(p):
    '''
    member_declarator : declarator
                      | declarator '=' constant_expression
                      | declarator '=' '0'
    '''
    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 4:
        p[1].parse_initializer(p[3])
        p[0] = p[1]
    
    assert isinstance(p[0], StructBase)

######################################################################

######################################################################
# Declarators
######################################################################

#   def p_function_definition(p):
#       '''
#       function_definition : declarator function_body
#       '''
#       p[0] = p[1] + p[2]

def p_init_declarator_list(p):
    '''
    init_declarator_list : init_declarator
                         | init_declarator_list ',' init_declarator
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 3:
        p[0] = p[1] + [p[2]]

def p_init_declarator(p):
    '''
    init_declarator : declarator
                    | declarator initializer
    '''
    p[0] = p[1]

def p_initializer(p):
    '''
    initializer : '=' constant_expression
    '''
    p[0] = [2]

def p_declarator(p):
    '''
    declarator : direct_declarator
               | ptr_operator declarator
    '''
    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 3:
        p[2].parse_ptr_operator(p[1])
        p[0] = p[2]

    assert isinstance(p[0], StructBase)

def p_ptr_operator(p):
    '''
    ptr_operator : '&'
                 | '*'
                 | '*' CONST
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 3:
        p[0] = [p[1]] + [p[2]]

def p_direct_declarator(p):
    '''
    direct_declarator : declarator_id
                      | direct_declarator '(' parameter_declaration_list ')'
                      | direct_declarator '[' constant_expression ']'
                      | direct_declarator '(' parameter_declaration_list ')' CONST
    '''
    if len(p) == 2:
        p[0] = VariableStruct(name = p[1])
    elif len(p) == 5:
        if p[2] == '(':
            p[0] = FunctionStruct(name = p[1].name)
            p[0].parameter_list = p[3]
        elif p[2] == '[':
            p[1].name += reduce(operator.add, p[2:5])
            p[0] = p[1]
    elif len(p) == 6:
        p[0] = FunctionStruct(name = p[1].name)
        p[0].const = True
        p[0].parameter_list = p[3]
   
    assert isinstance(p[0], StructBase)

def p_declarator_id(p):
    '''
    declarator_id : ID
                  | '~' ID
    '''
    p[0] = p[1]

def p_parameter_declaration_list(p):
    '''
    parameter_declaration_list : 
                               | parameter_declaration
                               | parameter_declaration_list ',' parameter_declaration
    '''
    if len(p) == 1:
        p[0] = []
    elif len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = p[1] + [p[3]]

def p_parameter_declarton(p):
    '''
    parameter_declaration : decl_specifier_seq
                          | decl_specifier_seq declarator
                          | decl_specifier_seq declarator '=' constant_expression
    '''
    if len(p) == 2:
        p[0] = VariableStruct()
    else:
        assert isinstance(p[2], VariableStruct)
        p[0] = p[2]

    p[0].parse_decl_specifier_seq(p[1])

    if len(p) == 5:
        p[0].default_value = p[4]

#   def p_function_body(p):
#       '''
#       function_body :
#       '''
#       pass

######################################################################

######################################################################
# Declarations
######################################################################

def p_declaration_seq(p):
    '''
    declaration_seq : declaration
                    | declaration_seq declaration
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 3:
        p[0] = p[1] + [p[2]]

def p_declaration(p):
    '''
    declaration : block_declaration
    '''
    p[0] = p[1]

def p_block_declaration(p):
    '''
    block_declaration : simple_declaration
    '''
    p[0] = p[1]

def p_simple_declaration(p):
    '''
    simple_declaration : decl_specifier_seq ';'
                       | decl_specifier_seq init_declarator_list ';'
    '''
    if len(p) == 3:
        if len(p[1]) == 1:
            assert isinstance(p[1][0], StructBase)
            p[0] = p[1][0]
        elif len(p[1]) == 3:
            assert p[1][0] == 'typedef'
            assert isinstance(p[1][1], ClassStruct) or isinstance(p[1][1], EnumStruct)
            p[1][1].name = p[1][2]
            p[0] = p[1][1]
        else:
            assert False
    elif len(p) == 4:
        import pdb
        pdb.set_trace()


def p_decl_specifier_seq(p):
    '''
    decl_specifier_seq : decl_specifier
                       | decl_specifier_seq decl_specifier
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 3:
        p[0] = p[1] + [p[2]]

    p[0] = filter(lambda item : item, p[0])

def p_decl_specifier(p):
    '''
    decl_specifier : storage_class_specifier
                   | type_specifier
                   | function_specifier
                   | FRIEND
                   | TYPEDEF
    '''
    if p[1] != 'friend':
        p[0] = p[1]

def p_storage_class_specifier(p):
    '''
    storage_class_specifier : AUTO
                            | REGISTER
                            | STATIC
                            | EXTERN
                            | MUTABLE
    '''
    pass

def p_function_specifier(p):
    '''
    function_specifier : INLINE
                       | VIRTUAL
                       | EXPLICIT
    '''
    if p[1] == 'virtual':
        p[0] = p[1]

def p_type_specifier(p):
    '''
    type_specifier : simple_type_specifier
                   | class_specifier
                   | enum_specifier
                   | cv_qualifier
    '''
    p[0] = p[1]

def p_simple_type_specifier(p):
    '''
    simple_type_specifier : ID
                          | CHAR
                          | WCHAR
                          | BOOL
                          | SHORT
                          | INT
                          | LONG
                          | SIGNED
                          | UNSIGNED
                          | FLOAT
                          | DOUBLE
                          | VOID
    '''
    p[0] = p[1]

def p_enum_specifier(p):
    '''
    enum_specifier : ENUM '{' '}'
                   | ENUM '{' enumerator_list '}'
                   | ENUM ID '{' '}'
                   | ENUM ID '{' enumerator_list '}'
    '''
    if len(p) == 4:
        p[0] = EnumStruct()
    elif len(p) == 5:
        if p[2] == '{':
            p[0] = EnumStruct()
            for item in p[3]:
                assert isinstance(item, Enumerator)
            p[0].item_list = p[3]
        else:
            p[0] = EnumStruct(name = p[2])
    elif len(p) == 6:
        p[0] = EnumStruct(name = p[2])
        for item in p[4]:
            assert isinstance(item, Enumerator)
        p[0].item_list = p[4]

def p_enumerator_list(p):
    '''
    enumerator_list : enumerator_def
                    | enumerator_list ',' enumerator_def
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = p[1] + [p[3]]

def p_enumerator_def(p):
    '''
    enumerator_def : ID
                   | ID '=' constant_expression
    '''
    if len(p) == 2:
        p[0] = Enumerator(name = p[1])
    elif len(p) == 4:
        p[0] = Enumerator(name = p[1], value = p[3])

######################################################################

######################################################################
# Templates
######################################################################

def p_template_declaration(p):
    '''
    template_declaration : TEMPLATE '<' template_parameter_list '>'
    '''
    p[0] = p[3]

def p_template_parameter_list(p):
    '''
    template_parameter_list : template_parameter
                            | template_parameter_list ',' template_parameter
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = p[1] + [p[3]]

def p_template_parameter(p):
    '''
    template_parameter : type_parameter
    '''
    p[0] = p[1]

def p_type_parameter(p):
    '''
    type_parameter : CLASS ID
                   | TYPENAME ID
    '''
    p[0] = p[2]

######################################################################

######################################################################
# Derived Classes
######################################################################

def p_base_clause(p):
    '''
    base_clause : ':' base_specifier_list
    '''
    p[0] = p[2]

def p_base_specifier_list(p):
    '''
    base_specifier_list : base_specifier
                        | base_specifier_list ',' base_specifier
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    elif len(p) == 4:
        p[0] = p[1] + [p[3]]

def p_base_specifier(p):
    '''
    base_specifier : access_specifier ID
                   | ID
    '''
    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 3:
        p[0] = p[2]

######################################################################

######################################################################
# Expressions
######################################################################

def p_constant_expression(p):
    '''
    constant_expression : ID
                        | DECIMAL_LITERAL
                        | FLOATING_LITERAL
                        | CHARACTER_LITERAL
    '''
    p[0] = p[1]

#ef p_id_expression(p):
#   '''
#   id_expression : ID
#                 | '~' ID
#   '''
#   if len(p) == 2:
#       p[0] = p[1]
#   elif len(p) == 3:
#       p[0] = p[1] + p[2]

######################################################################

######################################################################
# Common
######################################################################

def p_access_specifier(p):
    '''
    access_specifier : PUBLIC
                     | PROTECTED
                     | PRIVATE
    '''
    p[0] = p[1]

def p_class_key(p):
    '''
    class_key : CLASS
              | STRUCT
              | UNION
    '''
    assert p[1] != 'union'
    p[0] = p[1]

def p_cv_qualifier(p):
    '''
    cv_qualifier : CONST
                | VOLATILE
    '''
    if p[1] == 'const':
        p[0] = p[1]

def p_error(p):
    print "Syntax error at '%s'" % p.value

######################################################################

def ParseString(content):
    lex.lex()
#   lex.input(content)
#   for token in iter(lex.token, None):
#           print repr(token.type), repr(token.value)
    yacc.yacc()
    return yacc.parse(content)

class StructBase():
    pass

class ClassStruct(StructBase):
    def __init__(self, name = '', parent_list = []):
        self.name = name
        self.parent_list = parent_list
        self.member_list = []
        self.template_parameter_list = []
        self.default_visibility = False

class FunctionStruct(StructBase):
    def __init__(self, name = '', parameter_list = []):
        self.name = name
        self.virtual = False
        self.const = False
        self.pure = False
        self.parameter_list = parameter_list
        self.return_type = VariableStruct()

    def __repr__(self):
        tokens = []
        if self.virtual:
            tokens.append('virtual')
        tokens.append(repr(self.return_type))
        tokens.append(self.name)
        tokens.append('(' + ', '.join(self.parameter_list) + ')')
        if self.const:
            tokens.append('const')
        if self.pure:
            tokens.append('=')
            tokens.append('0')
        return ' '.join(tokens) + ';'

    def __eq__(self, other):
        return self.name == other.name and self.parameter_list == other.parameter_list and self.virtual == other.virtual and self.const == other.const and self.return_type == other.return_type and self.pure == other.pure

    def parse_ptr_operator(self, tokens):
        ParsePtrOperator(self.return_type, tokens)

    def parse_decl_specifier_seq(self, tokens):
        if 'virtual' in tokens:
            virtual_index = tokens.index('virtual')
            self.virtual = True
            ParseVariable(self.return_type, tokens[0 : virtual_index] + tokens[virtual_index + 1 :])
        else:
            ParseVariable(self.return_type, tokens)
    
    def parse_initializer(self, initializer):
        assert initializer == '0'
        self.pure = True

class VariableStruct(StructBase):
    def __init__(self, name = '', type = ''):
        self.name = name
        self.type = type
        self.const = False
        self.reference = False
        self.pointer = False
        self.const_pointer = False
        self.default_value = ''

    def __repr__(self):
        tokens = []
        if self.const:
            tokens.append('const')
        tokens.append(self.type)
        if self.pointer:
            tokens.append('*')
        if self.const_pointer:
            tokens.append('const')
        if self.reference:
            tokens.append('&')
        if self.name != '':
            tokens.append(self.name)
        if self.default_value != '':
            tokens.append('=')
            tokens.append(self.default_value)
        return ' '.join(tokens)

    def __eq__(self, other):
        return self.name == other.name and self.type == other.type and self.const == other.const and self.pointer == other.pointer and self.reference == other.reference and self.const_pointer == other.const_pointer and self.default_value == other.default_value

    def parse_ptr_operator(self, tokens):
        ParsePtrOperator(self, tokens)

    def parse_decl_specifier_seq(self, tokens):
        assert not 'virtual' in tokens
        ParseVariable(self, tokens)

    def parse_initializer(self, initializer):
        self.default_value = initializer

class Enumerator():
    def __init__(self, name, value = ''):
        self.name = name
        self.value = value

    def __eq__(self, other):
        return self.name == other.name and self.value == other.value

class EnumStruct(StructBase):
    def __init__(self, name = ''):
        self.name = name
        self.item_list = []

    def __eq__(self, other):
        return self.name == other.name and self.item_list == other.item_list
